package com.zhangfb95.crosschain.infra.controller.starter.httplog;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author zhangfb
 */
@Slf4j
public class DefaultHandlerInterceptor implements HandlerInterceptor {

    private final StarterProperties properties;
    private static final String START_TIME_KEY = "startTime";

    public DefaultHandlerInterceptor(StarterProperties properties) {
        this.properties = properties;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (properties.canLog(request.getRequestURI())) {
            request.setAttribute(START_TIME_KEY, System.currentTimeMillis());
            Util.setApiUrl(request.getRequestURI());
            if (properties.getMdcHttpHeaderMap() != null) {
                properties.getMdcHttpHeaderMap().forEach(Util::set);
            }
            logRequest(request);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) {
        if (properties.canLog(request.getRequestURI())) {
            long executeTime = getExecuteTime(request);
            logResponse(response, executeTime);
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                Exception ex) {
        Util.removeApiUrl();
        if (properties.getMdcHttpHeaderMap() != null) {
            properties.getMdcHttpHeaderMap().forEach((key, value) -> {
                Util.remove(key);
            });
        }
    }

    /**
     * 记录请求日志
     */
    private void logRequest(HttpServletRequest request) {
        Map<String, Object> headers = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            headers.put(headerName, request.getHeader(headerName));
        }
        Map<String, String[]> paramMap = request.getParameterMap();
        String paramStr = wrapParamMap(paramMap);
        if (Objects.equals(properties.getLevel(), "DEBUG")) {
            log.debug("Request URI: {}, Method: {}, Headers: {}, paramMap: {}",
                    request.getRequestURI(), request.getMethod(), headers, paramStr);
        } else {
            log.info("Request URI: {}, Method: {}, Headers: {}, paramMap: {}",
                    request.getRequestURI(), request.getMethod(), headers, paramStr);
        }
    }

    private static String wrapParamMap(Map<String, String[]> paramMap) {
        if (paramMap == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder("{");
        for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
            String value = "";
            if (entry.getValue() != null) {
                value = String.join(", ", entry.getValue());
            }
            sb.append(entry.getKey()).append("=").append("[").append(value).append("], ");
        }
        sb.append("}");
        return sb.toString();
    }

    /**
     * 记录返回日志
     */
    private void logResponse(HttpServletResponse response, long executeTime) {
        Map<String, Object> headers = new HashMap<>();
        for (String headerName : response.getHeaderNames()) {
            headers.put(headerName, response.getHeader(headerName));
        }
        if (Objects.equals(properties.getLevel(), "DEBUG")) {
            log.debug("Response Status: {}, Spend Time: {} ms, Headers: {}", response.getStatus(), executeTime, headers);
        } else {
            log.info("Response Status: {}, Spend Time: {} ms, Headers: {}", response.getStatus(), executeTime, headers);
        }
    }

    /**
     * 计算花费时间
     */
    private long getExecuteTime(HttpServletRequest request) {
        Long startTime = (Long) request.getAttribute(START_TIME_KEY);
        request.removeAttribute(START_TIME_KEY);

        long executeTime;
        if (startTime != null) {
            long endTime = System.currentTimeMillis();
            executeTime = endTime - startTime;
        } else {
            executeTime = -1L;
        }
        return executeTime;
    }

    /**
     * 获取指定请求头
     */
    private String getHeader(HttpServletRequest request, String key) {
        Enumeration<String> headers = request.getHeaders(key);
        if (headers != null && headers.hasMoreElements()) {
            return headers.nextElement();
        }
        return "";
    }
}
